/*
 * File:   geometry.h
 * Author: gustavo
 *
 * Created on September 5, 2011, 5:05 AM
 */

#ifndef GEOMETRY_H
#define	GEOMETRY_H

#ifdef	__cplusplus
extern "C" {
#endif

#include <util/list.h>

#include "../object/object.h"

	typedef enum {
		IMP_ORIENTATION_IN_LEFT = 0,
		IMP_ORIENTATION_IN_RIGHT,
		IMP_ORIENTATION_COLLINEAR
		//IMP_ORIENTATION_BETWEEN
	} ImpOrientation;

	typedef enum {
		IMP_INTERSECT_OUT = 0,
		IMP_INTERSECT_ON,
		IMP_INTERSECT_IN
	} ImpIntersect;


	// = Types =================================================================
	typedef struct _imp_point ImpPoint;
	typedef struct _imp_point_class ImpPointClass;
	typedef struct _imp_vertex ImpVertex;
	typedef struct _imp_vertex_class ImpVertexClass;
	typedef struct _imp_segment ImpSegment;
	typedef struct _imp_segment_class ImpSegmentClass;
	typedef struct _imp_edge ImpEdge;
	typedef struct _imp_edge_class ImpEdgeClass;
	typedef struct _imp_triangle ImpTriangle;
	typedef struct _imp_triangle_class ImpTriangleClass;
	typedef struct _imp_rectangle ImpRectangle;
	typedef struct _imp_rectangle_class ImpRectangleClass;
	typedef struct _imp_surface ImpSurface;
	typedef struct _imp_surface_class ImpSurfaceClass;


	// = Point =================================================================
#define IMP_IS_POINT(obj)		(imp_object_is_from_class (obj, imp_point_get_class ()))
#define IMP_POINT(obj)			IMP_OBJECT_CAST (obj, ImpPoint, imp_point_get_class () )
#define IMP_POINT_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpPointClass, imp_point_get_class () )

	struct _imp_point {
		extends(ImpObject);
		real x;
		real y;
		real z;
		long int id;
	};

	struct _imp_point_class {
		extends(ImpObjectClass);
	};
	// -------------------------------------------------------------------------
	ImpPointClass * imp_point_get_class(void);
	ImpPoint * imp_point_new(real x, real y, real z);
	void imp_point_set(ImpPoint * p, real x, real y, real z);
	// -------------------------------------------------------------------------
	real imp_point_distance2(ImpPoint * p1, ImpPoint * p2);
	real imp_point_distance(ImpPoint * p1, ImpPoint * p2);
	ImpOrientation imp_point_orientation(ImpPoint * a, ImpPoint * b, ImpPoint * c);
	boolean imp_point_in_left(ImpPoint * a, ImpPoint * b, ImpPoint * c);
	boolean imp_point_are_collinear(ImpPoint * a, ImpPoint * b, ImpPoint * c);
	boolean imp_point_in_right(ImpPoint * a, ImpPoint * b, ImpPoint * c);
	boolean imp_point_in_rect(ImpPoint * a, ImpPoint * b, ImpPoint * c);
	ImpPoint imp_point_mid_point(ImpPoint * a, ImpPoint * b);
	real imp_point_slope(ImpPoint * p1, ImpPoint * p2);
	// -------------------------------------------------------------------------
	void imp_point_print(ImpPoint * p);

	// = Vertex ================================================================
#define IMP_IS_VERTEX(obj)		(imp_object_is_from_class (obj, imp_vertex_get_class ()))
#define IMP_VERTEX(obj)			IMP_OBJECT_CAST (obj, ImpVertex, imp_vertex_get_class () )
#define IMP_VERTEX_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpVertexClass, imp_vertex_get_class () )

	struct _imp_vertex {
		extends(ImpPoint);
		List * segments;
		int id;

		//real x;
		//real y;

	};

	struct _imp_vertex_class {
		extends(ImpPointClass);
	};
	// -------------------------------------------------------------------------
	ImpVertexClass * imp_vertex_get_class(void);
	ImpVertex * imp_vertex_new(real x, real y, real z);
	void imp_vertex_set(ImpVertex * v, real x, real y, real z);
	void imp_vertex_set_count(long int c);
	// -------------------------------------------------------------------------
	ImpVertex * imp_vertex_new_from_point(ImpPoint p);
	real imp_vertex_compare(ImpVertex * v1, ImpVertex * v2);
	void imp_vertex_add_segment(ImpVertex * v, ImpSegment * s);
	ImpSegment * imp_vertex_remove_segment(ImpVertex * v, ImpSegment * s);
	boolean imp_vertex_have_segment(ImpVertex * v, ImpSegment * s);
	// -------------------------------------------------------------------------
	void imp_vertex_print(ImpVertex * v);

	// = Segment ===============================================================
#define IMP_IS_SEGMENT(obj)		(imp_object_is_from_class (obj, imp_segment_get_class ()))
#define IMP_SEGMENT(obj)			IMP_OBJECT_CAST (obj, ImpSegment, imp_segment_get_class () )
#define IMP_SEGMENT_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpSegmentClass, imp_segment_get_class () )

	struct _imp_segment {
		extends(ImpObject);
		ImpVertex * v[2];
		real slope;
	};

	struct _imp_segment_class {
		extends(ImpObjectClass);
	};
	// -------------------------------------------------------------------------
	ImpSegmentClass * imp_segment_get_class(void);
	ImpSegment * imp_segment_new(ImpVertex * v1, ImpVertex * v2);
	void imp_segment_set(ImpSegment * s, ImpVertex * v1, ImpVertex * v2);
	// -------------------------------------------------------------------------
	real imp_segment_length2(ImpSegment * s);
	real imp_segment_length(ImpSegment * s);
	ImpVertex * imp_segment_get_common_vertex(ImpSegment * s1, ImpSegment * s2);
	real imp_segment_compare_min_y(ImpSegment * s1, ImpSegment * s2);
#define imp_segment_have_vertex( s, vv ) ( (s)->v[0] == (vv) || (s)->v[1] == (vv) )
#define imp_segment_are_connected( s1, s2 )	( (s1)->v[0] == (s2)->v[0] || (s1)->v[0] == (s2)->v[1] || (s1)->v[1] == (s2)->v[0] || (s1)->v[1] == (s2)->v[1] )
#define imp_segment_connect(_s, _v1, _v2) ( ( (_s)->v[0] == _v1 && (_s)->v[1] == _v2) || ( (_s)->v[0] == _v2 && (_s)->v[1] == _v1) )

	// = Edge ==================================================================
#define IMP_IS_EDGE(obj)		(imp_object_is_from_class (obj, imp_edge_get_class ()))
#define IMP_EDGE(obj)			IMP_OBJECT_CAST (obj, ImpEdge, imp_edge_get_class () )
#define IMP_EDGE_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpEdgeClass, imp_edge_get_class () )

	struct _imp_edge {
		extends(ImpSegment);

		List * faces;
	};

	struct _imp_edge_class {
		extends(ImpObjectClass);

	};
	// -------------------------------------------------------------------------
	ImpEdgeClass * imp_edge_get_class(void);
	ImpEdge * imp_edge_new(ImpVertex * v1, ImpVertex * v2);
	void imp_edge_set(ImpEdge * e, ImpVertex * v1, ImpVertex * v2);
	// -------------------------------------------------------------------------
	void imp_edge_add_triangle(ImpEdge * e, ImpTriangle * t);
	ImpTriangle * imp_edge_remove_triangle(ImpEdge * e, ImpTriangle * t);
	inline void imp_edge_extract_points(ImpEdge * e1, ImpPoint ** p1, ImpPoint ** p2);
	inline void imp_edge_extract_vertices(ImpEdge * e1, ImpVertex ** v1, ImpVertex ** v2);
	// -------------------------------------------------------------------------
	void imp_edge_print(ImpEdge * e);
#define imp_edge_have_vertex( e, v )( imp_segment_have_vertex( IMP_SEGMENT(e), (v)) )

	// = Triangle ==============================================================
#define IMP_IS_TRIANGLE(obj)		(imp_object_is_from_class (obj, imp_triangle_get_class ()))
#define IMP_TRIANGLE(obj)			IMP_OBJECT_CAST (obj, ImpTriangle, imp_triangle_get_class () )
#define IMP_TRIANGLE_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpTriangleClass, imp_triangle_get_class () )

	struct _imp_triangle {
		extends(ImpObject);
		ImpEdge * e[3];
		long int id;

	};

	struct _imp_triangle_class {
		extends(ImpObjectClass);
	};
	// -------------------------------------------------------------------------
	ImpTriangleClass * imp_triangle_get_class(void);
	ImpTriangle * imp_triangle_new(ImpEdge * e1, ImpEdge * e2, ImpEdge * e3);
	void imp_triangle_set(ImpTriangle * t, ImpEdge * e1, ImpEdge * e2, ImpEdge * e3);
	void imp_triangle_set_count(long int c);
	// -------------------------------------------------------------------------
	void imp_triangle_extract(ImpTriangle * t, ImpEdge * e, ImpVertex ** v1, ImpVertex ** v2, ImpVertex ** v3, ImpEdge ** e1, ImpEdge ** e2, ImpEdge ** e3);
	void imp_triangle_extract_vertices(ImpTriangle * t, ImpVertex ** v1, ImpVertex ** v2, ImpVertex ** v3);
	void imp_triangle_extract_vertices_ord(ImpTriangle * t, ImpVertex * v, ImpVertex ** v1, ImpVertex ** v2, ImpVertex ** v3);
	void imp_triangle_extract_vertices_and_edges(ImpTriangle * t, ImpVertex ** v1, ImpVertex ** v2, ImpVertex ** v3, ImpEdge ** e1, ImpEdge ** e2, ImpEdge ** e3);
	boolean imp_triangle_have_vertex(ImpTriangle * t, ImpVertex * v);
	ImpVertex * imp_triangle_get_opposite_vertex(ImpTriangle * t, ImpEdge * e);
	ImpEdge * imp_triangle_get_opposite_edge(ImpTriangle * t, ImpVertex * v);
	ImpTriangle * imp_triangle_get_opposite_triangle(ImpTriangle * t, ImpEdge * e);
	ImpEdge * imp_triangle_get_shared_edge(ImpTriangle *T1, ImpTriangle *T2);
	ImpTriangle * imp_triangle_get_neighbor(ImpTriangle * T, ImpEdge * e);
	ImpEdge * imp_triangle_get_edge_that_connects_vertices(ImpTriangle * T, ImpVertex * v1, ImpVertex * v2);
	ImpPoint imp_triangle_barycenter(ImpTriangle * t);
	ImpPoint * imp_triangle_circumcircle_center(ImpTriangle * t);
	ImpIntersect imp_triangle_intersect_point(ImpTriangle * t, ImpPoint * p, ImpEdge ** eon);
	ImpIntersect imp_triangle_circle_intersect_point(ImpTriangle * t, ImpPoint * p);
	real imp_triangle_get_area(ImpTriangle * T);
	void imp_triangle_print(ImpTriangle * t);
	boolean imp_triangle_is_ok(ImpTriangle * t);
#define imp_triangle_have_edge( T, e ) ( T->e[0] == e || T->e[1] == e || T->e[2] == e )

	// = Rectangle =============================================================
#define IMP_IS_RECTANGLE(obj)		(imp_object_is_from_class (obj, imp_rectangle_get_class ()))
#define IMP_RECTANGLE(obj)			IMP_OBJECT_CAST (obj, ImpRectangle, imp_rectangle_get_class () )
#define IMP_RECTANGLE_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpRectangleClass, imp_rectangle_get_class () )

	struct _imp_rectangle {
		extends(ImpObject);

		ImpEdge * e[4];
	};

	struct _imp_rectangle_class {
		extends(ImpObjectClass);

	};

	// = Surface ===============================================================
#define IMP_IS_SURFACE(obj)		(imp_object_is_from_class (obj, imp_surface_get_class ()))
#define IMP_SURFACE(obj)			IMP_OBJECT_CAST (obj, ImpSurface, imp_surface_get_class () )
#define IMP_SURFACE_CLASS(klass)	IMP_OBJECT_CLASS_CAST (klass, ImpSurfaceClass, imp_surface_get_class () )

	struct _imp_surface {
		extends(ImpObject);

		List * vertices;
		List * edges;
		//List * triangles;
		List * faces;

	};

	struct _imp_surface_class {
		extends(ImpObjectClass);

	};

	ImpSurfaceClass * imp_surface_get_class(void);
	ImpSurface * imp_surface_new(void);
	ImpVertex * imp_surface_add_vertex(ImpSurface * s, ImpVertex * v);
	ImpEdge * imp_surface_add_edge(ImpSurface * s, ImpEdge * e);
	ImpTriangle * imp_surface_add_triangle(ImpSurface * s, ImpTriangle * t);
	ImpRectangle * imp_surface_add_rectangle(ImpSurface * s, ImpRectangle * r);
	ImpVertex * imp_surface_remove_vertex(ImpSurface * s, ImpVertex * v);
	ImpEdge * imp_surface_remove_edge(ImpSurface * s, ImpEdge * e);
	ImpTriangle * imp_surface_remove_triangle(ImpSurface * s, ImpTriangle * t);
	void imp_surface_remove_orfans_vertices(ImpSurface * surf);
	void imp_surface_remove_orfans_edges(ImpSurface * surf);
	void imp_surface_remove_all_elements(ImpSurface * s);
	void imp_surface_remove_all_elements_full(ImpSurface * s);


#ifdef	__cplusplus
}
#endif

#endif	/* GEOMETRY_H */

